import React, { useState, useMemo, useCallback } from 'react';

/**
 * useMemo Hook示例组件
 * 
 * 这个组件展示了React useMemo Hook的多种使用场景：
 * 1. 缓存昂贵的计算结果
 * 2. 避免不必要的重新计算
 * 3. 记忆引用类型值以避免不必要的重新渲染
 * 4. 与useCallback的比较
 * 5. 条件记忆
 */
function UseMemoExample() {
  // 状态
  const [numbers, setNumbers] = useState([10, 20, 30, 40, 50]);
  const [multiplier, setMultiplier] = useState(2);
  // 移除未使用的状态
  const [darkMode, setDarkMode] = useState(false);
  
  // 用于触发重新渲染但不影响计算的状态
  const [rerenderCounter, setRerenderCounter] = useState(0);
  
  // 模拟耗时计算的函数
  const expensiveCalculation = (nums, mult) => {
    console.log('执行昂贵计算...');
    // 模拟耗时操作
    const startTime = performance.now();
    while (performance.now() - startTime < 100) {
      // 人为延迟100ms
    }
    
    return nums.map(num => num * mult);
  };
  
  // 示例1: 使用useMemo缓存昂贵计算结果
  const multipliedNumbers = useMemo(() => {
    return expensiveCalculation(numbers, multiplier);
  }, [numbers, multiplier]); // 只有当numbers或multiplier变化时才重新计算
  
  // 示例2: 不使用useMemo的计算（每次渲染都会执行）
  const multipliedNumbersNoMemo = expensiveCalculation(numbers, multiplier);
  
  // 示例3: 使用useMemo记忆对象引用
  const user = useMemo(() => {
    return {
      id: 1,
      name: '张三',
      preferences: {
        theme: darkMode ? 'dark' : 'light'
      }
    };
  }, [darkMode]); // 只有当darkMode变化时才创建新对象
  
  // 示例4: 不使用useMemo的对象（每次渲染都会创建新对象）
  const userNoMemo = {
    id: 1,
    name: '张三',
    preferences: {
      theme: darkMode ? 'dark' : 'light'
    }
  };
  
  // 示例5: 条件记忆 - 只有当数组长度超过阈值时才使用昂贵算法
  const sortedNumbers = useMemo(() => {
    console.log('排序数组...');
    
    // 如果数组较小，使用简单排序
    if (numbers.length <= 10) {
      return [...numbers].sort((a, b) => a - b);
    }
    
    // 对于大数组，模拟更复杂的排序算法
    const startTime = performance.now();
    while (performance.now() - startTime < 50) {
      // 人为延迟50ms
    }
    return [...numbers].sort((a, b) => a - b);
  }, [numbers]);
  
  // 示例6: useMemo vs useCallback
  // useCallback记忆函数本身
  const handleClick = useCallback(() => {
    console.log('按钮被点击');
  }, []);
  
  // useMemo可以用来记忆任何值，包括函数
  const handleClickWithMemo = useMemo(() => {
    return () => {
      console.log('使用useMemo记忆的函数被调用');
    };
  }, []);
  
  // 添加数字到数组
  const addNumber = () => {
    const newNumber = Math.floor(Math.random() * 100);
    setNumbers([...numbers, newNumber]);
  };
  
  // 改变乘数
  const changeMultiplier = (e) => {
    setMultiplier(Number(e.target.value));
  };
  
  // 触发重新渲染
  const triggerRerender = () => {
    setRerenderCounter(count => count + 1);
  };
  
  // 切换暗模式
  const toggleDarkMode = () => {
    setDarkMode(prev => !prev);
  };
  
  // 注释掉未使用的渲染时间计算
  // const renderStartTime = performance.now();
  // const renderEndTime = performance.now();
  // const renderTime = renderEndTime - renderStartTime;
  
  return (
    <div className="hook-example" style={{
      backgroundColor: darkMode ? '#333' : '#fff',
      color: darkMode ? '#fff' : '#333',
      padding: '20px',
      transition: 'background-color 0.3s, color 0.3s'
    }}>
      <h2>useMemo Hook 示例</h2>
      
      <section>
        <h3>重新渲染控制</h3>
        <p>重新渲染计数: {rerenderCounter}</p>
        <button onClick={triggerRerender}>触发重新渲染</button>
        <button onClick={toggleDarkMode}>
          切换到{darkMode ? '浅色' : '深色'}模式
        </button>
        <p className="note">
          点击"触发重新渲染"按钮会更新状态，导致组件重新渲染，
          但不会影响useMemo缓存的值，除非它们的依赖项发生变化。
        </p>
      </section>
      
      <div className="memo-comparison">
        <section>
          <h3>1. 缓存昂贵计算结果</h3>
          <div className="controls">
            <label>
              乘数:
              <input
                type="number"
                value={multiplier}
                onChange={changeMultiplier}
                min="1"
                max="10"
              />
            </label>
            <button onClick={addNumber}>添加随机数</button>
          </div>
          
          <div className="results">
            <div>
              <h4>使用 useMemo:</h4>
              <p>原始数组: {numbers.join(', ')}</p>
              <p>乘以 {multiplier}: {multipliedNumbers.join(', ')}</p>
              <p className="performance-note">
                只有当numbers或multiplier变化时才会重新计算
              </p>
            </div>
            
            <div>
              <h4>不使用 useMemo:</h4>
              <p>原始数组: {numbers.join(', ')}</p>
              <p>乘以 {multiplier}: {multipliedNumbersNoMemo.join(', ')}</p>
              <p className="performance-note">
                每次组件重新渲染时都会重新计算
              </p>
            </div>
          </div>
        </section>
        
        <section>
          <h3>2. 记忆对象引用</h3>
          <div>
            <h4>使用 useMemo 的对象:</h4>
            <pre>{JSON.stringify(user, null, 2)}</pre>
            <p className="performance-note">
              只有当darkMode变化时才会创建新对象
            </p>
          </div>
          
          <div>
            <h4>不使用 useMemo 的对象:</h4>
            <pre>{JSON.stringify(userNoMemo, null, 2)}</pre>
            <p className="performance-note">
              每次渲染时都会创建新对象
            </p>
          </div>
        </section>
        
        <section>
          <h3>3. 条件记忆</h3>
          <p>排序后的数组: {sortedNumbers.join(', ')}</p>
          <p className="performance-note">
            根据数组大小使用不同的算法，但只有当numbers变化时才会重新计算
          </p>
        </section>
        
        <section>
          <h3>4. useMemo vs useCallback</h3>
          <button onClick={handleClick}>
            useCallback按钮
          </button>
          <button onClick={handleClickWithMemo}>
            useMemo按钮
          </button>
          <p className="explanation">
            useCallback记忆函数本身，而useMemo记忆函数的返回值。
            在这个例子中，两者都用于记忆函数，效果类似。
            实际上，<code>useCallback(fn, deps)</code> 等价于 <code>useMemo(() => fn, deps)</code>。
          </p>
        </section>
      </div>
      
      <div className="explanation">
        <h3>useMemo Hook 说明:</h3>
        <ul>
          <li><code>const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])</code></li>
          <li>只有当依赖项数组中的值变化时，才会重新计算记忆值</li>
          <li>主要用途:
            <ul>
              <li>避免在每次渲染时重新进行昂贵的计算</li>
              <li>保持引用相等性，避免不必要的重新渲染</li>
              <li>优化依赖于复杂计算的渲染性能</li>
            </ul>
          </li>
          <li>空依赖数组 <code>[]</code> 意味着记忆值只在组件挂载时计算一次</li>
          <li>不要过度使用 - 只在计算真正昂贵或需要引用稳定性时才需要</li>
        </ul>
      </div>
    </div>
  );
}

export default UseMemoExample;